/**
 * $version:  0.1 
 * $Date: 2012-04-05 
 *
 * Copyright (C) 2010-2012 Jawa Software. All rights reserved.
 *
 */

package org.jawa;

import java.io.FileNotFoundException;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import org.apache.commons.lang.time.DateFormatUtils;
import org.jawa.branch.BranchManager;
import org.jawa.cluster.ClusterManager;
import org.jawa.cluster.NodeID;
import org.jawa.database.DbConnectionManager;
import org.jawa.user.UserManager;
import org.jawa.util.InitializationException;
import org.jawa.util.JawaConstants;
import org.jawa.util.JawaGlobals;
import org.jawa.util.Log;
import org.jawa.util.cache.CacheFactory;

public class JawaServer implements Serializable {

	//服务器实例
	private static JawaServer instance;

	//服务器名称
	private String name;

	//服务器IP地址
	private String host;

	//服务器版本
	private Version version;

	//服务器最后一次启动时间
	private Date startDate;

	//服务器是否初始化
	private boolean initialized = false;

	//服务器是否已经启动
	private boolean started = false;

	//是否已安装.
	private boolean setupMode = true;

	//服务器信息.
	private JawaServerInfo serverInfo;

	//服务器在集群中的唯一标识
	private NodeID nodeID;

	private static final NodeID DEFAULT_NODE_ID = NodeID.getInstance(new byte[0]);

	private ClassLoader loader;

	//在服务器启动或停止的时候发布事件.
	private List<JawaServerListener> listeners = new CopyOnWriteArrayList<JawaServerListener>();

	/**
	 * 返回服务器的单一实例.
	 * 
	 * @return 服务器的单一实例.
	 */
	public static JawaServer getInstance() {
		return instance;
	}

	/**
	 * 创建服务器并启动.
	 */
	public JawaServer() {
		if (instance != null) {
			throw new IllegalStateException("服务器已经启动");
		}
		instance = this;
		//启动服务器
		start();
	}

	/**
	 * 初始化服务器.
	 * @throws FileNotFoundException
	 */
	private void initialize() {

		name = JawaGlobals.getProperty("server.domain", "127.0.0.1").toLowerCase();

		try {
			host = InetAddress.getLocalHost().getHostName();
		} catch (UnknownHostException ex) {
			Log.warn("无法确定本地主机名.", ex);
		}

		version = new Version(1, 0, 0, Version.ReleaseStatus.Release, -1);

		if ("true".equals(JawaGlobals.getXMLProperty("setup"))) {
			setupMode = false;
		}

		loader = Thread.currentThread().getContextClassLoader();

		// 初始化本地缓存
		try {
			CacheFactory.initialize();
		} catch (InitializationException e) {
			e.printStackTrace();
			Log.error(e.getMessage(), e);
		}

		//尝试启动集群缓存
		ClusterManager.setClusteringEnabled(true);

		initialized = true;
	}

	/**
	 * 完成安装过程。
	 */
	public void finishSetup() {
		if (!setupMode) {
			return;
		}
		if ("true".equals(JawaGlobals.getXMLProperty("setup"))) {
			setupMode = false;
		}

		/*if ("true".equals(JawaGlobals.getXMLProperty("setup"))) {
		    // Set the new server domain assigned during the setup process
		    name = JawaGlobals.getProperty("xmpp.domain").toLowerCase();
		    serverInfo.setDomain(name);

		    // Update certificates (if required)
		    try {
		        // Check if keystore already has certificates for current domain
		        KeyStore ksKeys = SSLConfig.getKeyStore();
		        boolean dsaFound = CertificateManager.isDSACertificate(ksKeys, name);
		        boolean rsaFound = CertificateManager.isRSACertificate(ksKeys, name);

		        // No certificates were found so create new self-signed certificates
		        if (!dsaFound) {
		            CertificateManager.createDSACert(ksKeys, SSLConfig.getKeyPassword(),
		                    name + "_dsa", "cn=" + name, "cn=" + name, "*." + name);
		        }
		        if (!rsaFound) {
		            CertificateManager.createRSACert(ksKeys, SSLConfig.getKeyPassword(),
		                    name + "_rsa", "cn=" + name, "cn=" + name, "*." + name);
		        }
		        // Save new certificates into the key store
		        if (!dsaFound || !rsaFound) {
		            SSLConfig.saveStores();
		        }

		    } catch (Exception e) {
		        Log.error("Error generating self-signed certificates", e);
		    }

		    // Initialize list of admins now (before we restart Jetty)
		    AdminManager.getInstance().getAdminAccounts();

		    Thread finishSetup = new Thread() {
		        @Override
				public void run() {
		            try {
		                if (isStandAlone()) {
		                    // Always restart the HTTP server manager. This covers the case
		                    // of changing the ports, as well as generating self-signed certificates.
		                
		                    // Wait a short period before shutting down the admin console.
		                    // Otherwise, the page that requested the setup finish won't
		                    // render properly!
		                    Thread.sleep(1000);
		                    ((AdminConsolePlugin) pluginManager.getPlugin("admin")).restart();
		//                            ((AdminConsolePlugin) pluginManager.getPlugin("admin")).shutdown();
		//                            ((AdminConsolePlugin) pluginManager.getPlugin("admin")).startup();
		                }

		                verifyDataSource();
		                // First load all the modules so that modules may access other modules while
		                // being initialized
		                loadModules();
		                // Initize all the modules
		                initModules();
		                // Start all the modules
		                startModules();
		            }
		            catch (Exception e) {
		                e.printStackTrace();
		                Log.error(e.getMessage(), e);
		                shutdownServer();
		            }
		        }
		    };
		    // Use the correct class loader.
		    finishSetup.setContextClassLoader(loader);
		    finishSetup.start();
		    // We can now safely indicate that setup has finished
		    setupMode = false;

		    // Update server info
		    xmppServerInfo = new XMPPServerInfoImpl(name, host, version, startDate, getConnectionManager());
		}*/
	}

	/**
	 * 启动服务器.
	 */
	public void start() {
		try {
			initialize();

			startDate = new Date();

			serverInfo = new JawaServerInfo(name, host, version, startDate);

			//如果已正确安装
			if (!setupMode) {
				// FIXME:
				verifyDataSource();
			}

			String startupBanner = "[服务器]  版本: " + version.getVersionString() + " ( "
					+ (DateFormatUtils.format(new Date(), JawaConstants.DATETIME_FORMAT)) + " )";
			System.out.println(startupBanner);

			started = true;

		} catch (Exception e) {
			Log.error(e);
			System.out.println(("服务器启动失败"));
		}
	}

	/**
	 * 返回服务器主机名.
	 * @return
	 */
	public String getHostname() {
		return host;
	}

	/**
	 * 验证数据库连接情况.
	 */
	private void verifyDataSource() {
		java.sql.Connection conn = null;
		try {
			conn = DbConnectionManager.getConnection();
			PreparedStatement stmt = conn.prepareStatement("SELECT count(*) FROM app_id");
			ResultSet rs = stmt.executeQuery();
			rs.next();
			rs.close();
			stmt.close();
		} catch (Exception e) {
			System.err.println("数据库安装或配置错误: " + "请检查数据库的配置是否正确,在 " + "logs/error.log 文件中记录了详细的错误情况.");
			Log.error("数据库无法正常访问", e);
			throw new IllegalArgumentException(e);
		} finally {
			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					Log.error(e);
				}
			}
		}
	}

	/**
	* 返回该服务器在集群中的唯一标识.
	*
	* @return 服务器在集群中的唯一标识.
	*/
	public NodeID getNodeID() {
		return nodeID == null ? DEFAULT_NODE_ID : nodeID;
	}

	/**
	 * 设置该服务器在集群中的唯一标识
	 *
	 * @param 该服务器在集群中的唯一标识.
	 */
	public void setNodeID(NodeID nodeID) {
		this.nodeID = nodeID;
	}

	/**
	 * 服务器是否已正确安装.
	 * @return 服务器是否已正确安装.
	 */
	public boolean isSetupMode() {
		return setupMode;
	}

	/**
	* 服务器是否已经开始启动.
	* 
	* @return 服务器是否已经开始启动.
	*/
	public boolean isStarted() {
		return started;
	}

	/**
	 * 返回服务器信息.
	 * @return 服务器信息
	 */
	public JawaServerInfo getServerInfo() {
		if (!initialized) {
			throw new IllegalStateException("服务器没有初始化!");
		}
		return serverInfo;
	}

	/**
	 * 返回服务器版本.
	 * @return 服务器版本
	 */
	public Version getVersion() {
		return version;
	}

	/**
	 * 增加服务器事件监听.
	 * @param jawaServerListener
	 */
	public void addServerListener(JawaServerListener jawaServerListener) {
		listeners.add(jawaServerListener);
	}

	/**
	 * 删除服务器事件监听.
	 *
	 * @param 服务器事件.
	 */
	public void removeServerListener(JawaServerListener listener) {
		listeners.remove(listener);
	}

	/**
	 * 服务器停止.
	 */
	public void stop() {
		ClusterManager.shutdown();
		DbConnectionManager.destroyConnectionProvider();
		Log.info("服务器停止!");

	}

	/**
	 * 返回用户管理类.	
	 * @return UserManager 实例
	 */
	public UserManager getUserManager() {
		return UserManager.getInstance();
	}

	/**
	 * 返回部门管理类.	
	 * @return BranchManager 实例
	 */
	public BranchManager getBranchManager() {
		return BranchManager.getInstance();
	}
}
